/*
Continuous Testing Toolkit
Copyright (C) 2009 Paul R. Holser, Jr.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package org.infinitest.toolkit.runners;

import static java.util.Collections.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;

/**
 * Test classes run with this runner will have their inner test classes run as well, whether they be static or not.
 * A non-static inner class will therefore have access to the fixtures of the enclosing class.
 *
 * Each non-static inner class test will get instantiated and attached to a new instance of the enclosing class.
 * The {@link org.junit.Before}s of the enclosing class will be run before those of the inner class.
 * The {@link org.junit.After}s of the enclosing class will be run after those of the inner class.
 *
 * @author <a href="http://www.pholser.com">Paul Holser</a>
 */
public class EnclosedInners extends ParentRunner<Runner> {
    private final List<Runner> runners;

    /**
     * Called reflectively on the test class considered to be the "enclosing" class.  This should be a top-level
     * Java class.
     *
     * @param enclosing enclosing test class
     * @throws InitializationError if problems occur during runner creation
     */
    public EnclosedInners(Class<?> enclosing) throws InitializationError {
        super(enclosing);
        runners = new EnclosedInnersRunnerBuilder(enclosing).runners(enclosing, allEnclosedInnerClasses(enclosing));
    }

    @Override
    protected Description describeChild(Runner child) {
        return child.getDescription();
    }

    @Override
    protected List<Runner> getChildren() {
        return unmodifiableList(runners);
    }

    @Override
    protected void runChild(Runner child, RunNotifier notifier) {
        child.run(notifier);
    }

    private static Class<?>[] allEnclosedInnerClasses(Class<?> enclosing) {
        Set<Class<?>> enclosedInners = new HashSet<Class<?>>();
        for (Class<?> current = enclosing; current != null; current = current.getSuperclass())
            addAll(enclosedInners, (Class<?>[]) current.getDeclaredClasses());

        return enclosedInners.toArray(new Class<?>[enclosedInners.size()]);
    }
}
